/////////////////////////////////////////////////////////////////////////////
// This file is part of the "Java-DAP" project, a Java implementation
// of the OPeNDAP Data Access Protocol.
//
// Copyright (c) 2007 OPeNDAP, Inc.
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
/////////////////////////////////////////////////////////////////////////////
using System;
using System.IO;
using System.Runtime.Remoting.Messaging;
using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
using NDODS.DAP.Exceptions;
//UPGRADE_TODO: The package 'org.apache.commons.httpclient' could not be found. If it was not included in the conversion, there may be compiler issues. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1262'"
//using org.apache.commons.httpclient;
//UPGRADE_TODO: The type 'org.apache.commons.httpclient.methods.GetMethod' could not be found. If it was not included in the conversion, there may be compiler issues. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1262'"
//using GetMethod = org.apache.commons.httpclient.methods.GetMethod;

namespace NDODS.DAP
{
    /// <summary> Rewritten 1/15/07 jcaron to use HttpCLient library instead of jdk UrlConnection class.
    /// Need more robust redirect and authentication.
    /// <p/>
    /// This class provides support for common DODS client-side operations such as
    /// dereferencing a OPeNDAP URL, communicating network activity status
    /// to the user and reading local OPeNDAP objects.
    /// <p/>
    /// Unlike its C++ counterpart, this class does not store instances of the DAS,
    /// DDS, etc. objects. Rather, the methods <code>getDAS</code>, etc. return
    /// instances of those objects.
    /// 
    /// </summary>
    /// <author>  jehamby
    /// </author>
    /// <version>  $Revision: 15730 $
    /// </version>
    public class DConnect2
    {
        //UPGRADE_NOTE: Field 'EnclosingInstance' was added to class 'AnonymousClassCommand' to access its enclosing instance. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1019'"
        private class AnonymousClassCommand : Command
        {
            public AnonymousClassCommand(DConnect2 enclosingInstance)
            {
                InitBlock(enclosingInstance);
            }

            private void InitBlock(DConnect2 enclosingInstance)
            {
                this.enclosingInstance = enclosingInstance;
            }

            private DConnect2 enclosingInstance;

            public DConnect2 Enclosing_Instance
            {
                get { return enclosingInstance; }
            }

            public virtual void process(Stream is_Renamed)
            {
                sbyte[] buffer = new sbyte[4096]; // read the body fully
                while (SupportClass.ReadInput(is_Renamed, buffer, 0, buffer.Length) > 0)
                {
                    // empty
                }
            }
        }

        public static bool AllowSessions
        {
            set { allowSessions = value; }
        }

        //UPGRADE_NOTE: Respective javadoc comments were merged.  It should be changed in order to comply with .NET documentation conventions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1199'"
        /// <summary> Get the HttpClient object - a single instance is used.</summary>
        /// <summary> Set the HttpClient object - a single instance is used.</summary>
        public static HttpClient HttpClient
        {
            get { return _httpClient; }

            set { _httpClient = value; }
        }

        /// <returns> value of Last-Modified Header from last connection, may be null
        /// </returns>
        public virtual String LastModifiedHeader
        {
            get { return lastModified; }
        }

        /// <returns> value of X-Last-Modified-Invalid Header from last connection, may be null
        /// </returns>
        public virtual String LastModifiedInvalidHeader
        {
            get { return lastModifiedInvalid; }
        }

        /// <returns> value of Last-Extended Header from last connection, may be null
        /// </returns>
        public virtual String LastExtendedHeader
        {
            get { return lastExtended; }
        }

        /// <summary> Returns the DAS object from the dataset referenced by this object's URL.
        /// The DAS object is referred to by appending `.das' to the end of a DODS
        /// URL.
        /// 
        /// </summary>
        /// <returns> the DAS associated with the referenced dataset.
        /// </returns>
        /// <throws>  MalformedURLException if the URL given to the </throws>
        /// <summary>                               constructor has an error
        /// </summary>
        /// <throws>  IOException           if an error connecting to the remote server </throws>
        /// <throws>  ParseException        if the DAS parser returned an error </throws>
        /// <throws>  DASException          on an error constructing the DAS </throws>
        /// <throws>  DAP2Exception         if an error returned by the remote server </throws>
        public virtual DAS DAS
        {
            get
            {
                DASCommand command = new DASCommand(this);
                openConnection(urlString + ".das" + projString + selString, command);
                return command.das;
            }
        }

        private static bool allowSessions = false;

        private static HttpClient _httpClient;

        // default HttpClient
        //UPGRADE_NOTE: Synchronized keyword was removed from method 'initHttpClient'. Lock expression was added. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1027'"
        private void initHttpClient()
        {
            lock (this)
            {
                if (_httpClient != null)
                    return;
                MultiThreadedHttpConnectionManager connectionManager = new MultiThreadedHttpConnectionManager();
                _httpClient = new HttpClient(connectionManager);
            }
        }

        private String urlString; // The current DODS URL without Constraint Expression
        private String projString;

        /// <summary> The projection portion of the current DODS CE (including leading "?").</summary>
        private String selString;

        /// <summary> The selection portion of the current DODS CE (including leading "&").</summary>
        private bool acceptDeflate;

        /// <summary> Whether to accept compressed documents.</summary>
        // various stuff that comes from the HTTP headers
        private String lastModified = null;

        private String lastExtended = null;
        private String lastModifiedInvalid = null;
        private bool hasSession = false;

        private ServerVersion ver; // The OPeNDAP server version.

        private bool debugHeaders = false;

        public virtual void setServerVersion(String sv)
        {
            //System.out.println("ServerVersion made with String: " + sv);

            ver = new ServerVersion(sv);
            //System.out.println("ServerVersion.getMajor(): " + ver.getMajor());
            //System.out.println("ServerVersion.getMinor(): " + ver.getMinor());
        }

        public virtual void setServerVersion(int major, int minor)
        {
            //System.out.println("ServerVersion made with int,int: " + major + "," + minor);

            ver = new ServerVersion(major, minor);
            //System.out.println("ServerVersion.getMajor(): " + ver.getMajor());
            //System.out.println("ServerVersion.getMinor(): " + ver.getMinor());
        }


        /// <summary> Creates an instance bound to url which accepts compressed documents.
        /// 
        /// </summary>
        /// <param name="urlString">connect to this URL.
        /// </param>
        /// <throws>  FileNotFoundException thrown if <code>urlString</code> is not </throws>
        /// <summary>                               a valid URL, or a filename which exists on the system.
        /// </summary>
        public DConnect2(String urlString) : this(urlString, true)
        {
        }

        /// <summary> Creates an instance bound to url. If <code>acceptDeflate</code> is true
        /// then HTTP Request headers will indicate to servers that this client can
        /// accept compressed documents.
        /// 
        /// </summary>
        /// <param name="urlString">    Connect to this URL.  If urlString is not a valid URL,
        /// it is assumed to be a filename, which is opened.
        /// </param>
        /// <param name="acceptDeflate">true if this client can accept responses encoded
        /// with deflate.
        /// </param>
        /// <throws>  FileNotFoundException thrown if <code>urlString</code> is not </throws>
        /// <summary>                               a valid URL, or a filename which exists on the system.
        /// </summary>
        public DConnect2(String urlString, bool acceptDeflate)
        {
            int ceIndex = urlString.IndexOf('?');
            if (ceIndex != - 1)
            {
                this.urlString = urlString.Substring(0, (ceIndex) - (0));
                String expr = urlString.Substring(ceIndex);
                int selIndex = expr.IndexOf('&');
                if (selIndex != - 1)
                {
                    this.projString = expr.Substring(0, (selIndex) - (0));
                    this.selString = expr.Substring(selIndex);
                }
                else
                {
                    this.projString = expr;
                    this.selString = "";
                }
            }
            else
            {
                this.urlString = urlString;
                this.projString = this.selString = "";
            }
            this.acceptDeflate = acceptDeflate;
        }

        /// <summary> Returns the constraint expression supplied with the URL given to the
        /// constructor. If no CE was given this returns an empty <code>String</code>.
        /// <p/>
        /// Note that the CE supplied to one of this object's constructors is
        /// "sticky"; it will be used with every data request made with this object.
        /// The CE passed to <code>getData</code>, however, is not sticky; it is used
        /// only for that specific request. This method returns the sticky CE.
        /// 
        /// </summary>
        /// <returns> the constraint expression associated with this connection.
        /// </returns>
        public String CE()
        {
            return projString + selString;
        }

        /// <summary> Returns the URL supplied to the constructor. If the URL contained a
        /// constraint expression that is not returned.
        /// 
        /// </summary>
        /// <returns> the URL of this connection.
        /// </returns>
        public String URL()
        {
            return urlString;
        }

        /// <summary> Open a connection to the DODS server.
        /// 
        /// </summary>
        /// <param name="urlString">the URL to open.
        /// </param>
        /// <param name="command">  execute this command on the input stream
        /// </param>
        /// <throws>  IOException    if an IO exception occurred. </throws>
        /// <throws>  DAP2Exception  if the DODS server returned an error. </throws>
        /// <throws>  ParseException is cant parse the return </throws>
        private void openConnection(String urlString, Command command)
        {
            initHttpClient();
            GetMethod method = new GetMethod(urlString);
            method.setFollowRedirects(true);

            if (acceptDeflate)
                method.setRequestHeader(new Header("Accept-Encoding", "deflate"));

            // enable sessions
            if (allowSessions)
                method.setRequestHeader(new Header("X-Accept-Session", "true"));

            Stream is_Renamed = null;
            try
            {
                // Execute the method.

                int statusCode = _httpClient.executeMethod(method);

                if (statusCode == HttpStatus.SC_NOT_FOUND)
                {
                    throw new DAP2Exception(DAP2Exception.NO_SUCH_FILE, method.getStatusLine().toString());
                }

                if (statusCode == HttpStatus.SC_UNAUTHORIZED)
                {
                    throw new org.apache.commons.httpclient.auth.CredentialsNotAvailableException(
                        method.getStatusLine().toString());
                }

                if (statusCode != HttpStatus.SC_OK)
                {
                    throw new DAP2Exception("Method failed:" + method.getStatusLine());
                }

                // Get the response body.
                is_Renamed = method.getResponseBodyAsStream();

                // check if its an error
                Header header = method.getResponseHeader("content-description");
                if (header != null && header.getValue().equals("dods_error"))
                {
                    // create server exception object
                    DAP2Exception ds = new DAP2Exception();
                    // parse the Error object from stream and throw it
                    ds.parse(is_Renamed);
                    throw ds;
                }

                // parse the version
                Header h = method.getResponseHeader("xdods-server");
                String versionString = (h == null) ? null : h.getValue();
                ver = new ServerVersion(versionString);

                checkHeaders(method);

                // check for deflator
                h = method.getResponseHeader("content-encoding");
                String encoding = (h == null) ? null : h.getValue();
                if (encoding != null && encoding.Equals("deflate"))
                {
                    //UPGRADE_ISSUE: Class 'java.util.zip.InflaterInputStream' was not converted. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1000_javautilzipInflaterInputStream'"
                    //UPGRADE_ISSUE: Constructor 'java.util.zip.InflaterInputStream.InflaterInputStream' was not converted. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1000_javautilzipInflaterInputStream'"
                    InflaterInputStream inflater = new InflaterInputStream(is_Renamed);
                    is_Renamed = new BufferedStream(inflater, 10*1000); // JC added
                }

                command.process(is_Renamed);
            }
            catch (org.apache.commons.httpclient.auth.CredentialsNotAvailableException e)
            {
                throw e;
            }
            catch (HttpException e)
            {
                throw new DAP2Exception("Fatal protocol violation: " + e.getMessage());
            }
            catch (IOException e)
            {
                //UPGRADE_TODO: The equivalent in .NET for method 'java.lang.Throwable.getMessage' may return a different value. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1043'"
                throw new IOException("Fatal transport error: " + e.Message);
            }
            finally
            {
                // Release the connection.
                if (is_Renamed != null)
                    is_Renamed.Close();
                method.releaseConnection();
            }
        }


        public virtual void closeSession()
        {
            try
            {
                if (allowSessions && hasSession)
                {
                    openConnection(urlString + ".close", new AnonymousClassCommand(this));
                }
            }
                //UPGRADE_NOTE: Exception 'java.lang.Throwable' was converted to 'System.Exception' which has different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1100'"
            catch (Exception t)
            {
                // ignore
            }
        }

        /// <summary> Returns the <code>ServerVersion</code> of the last connection.
        /// 
        /// </summary>
        /// <returns> the <code>ServerVersion</code> of the last connection.
        /// </returns>
        public ServerVersion getServerVersion()
        {
            return ver;
        }

        private void checkHeaders(GetMethod method)
        {
            if (debugHeaders)
                Console.Out.WriteLine("\nOpenConnection Headers for " + method.getPath());
            if (debugHeaders)
                Console.Out.WriteLine("Status Line: " + method.getStatusLine());

            Header[] responseHeaders = method.getResponseHeaders();
            for (int i1 = 0; i1 < responseHeaders.length; i1++)
            {
                Header responseHeader = responseHeaders[i1];
                if (debugHeaders)
                    Console.Out.Write("  " + responseHeader);
                String key = responseHeader.getName();
                String value_Renamed = responseHeader.getValue();

                if (key.Equals("Last-Modified"))
                {
                    lastModified = value_Renamed;
                    if (debugHeaders)
                        Console.Out.WriteLine(" **found lastModified = " + lastModified);
                }
                else if (key.Equals("X-Last-Extended"))
                {
                    lastExtended = value_Renamed;
                    if (debugHeaders)
                        Console.Out.WriteLine(" **found lastExtended = " + lastExtended);
                }
                else if (key.Equals("X-Last-Modified-Invalid"))
                {
                    lastModifiedInvalid = value_Renamed;
                    if (debugHeaders)
                        Console.Out.WriteLine(" **found lastModifiedInvalid = " + lastModifiedInvalid);
                }
            }

            if (debugHeaders)
                Console.Out.WriteLine("OpenConnection Headers for " + method.getPath());
            HttpState state = _httpClient.getState();
            Cookie[] cookies = state.getCookies();

            if (cookies.length > 0)
            {
                if (debugHeaders)
                    Console.Out.WriteLine("Cookies= ");

                for (int i = 0; i < cookies.length; i++)
                {
                    Cookie cooky = cookies[i];
                    if (debugHeaders)
                        Console.Out.WriteLine("  " + cooky);
                    if (cooky.getName().equalsIgnoreCase("jsessionid"))
                        hasSession = true;
                }
            }
        }

        private interface Command
        {
            void process(Stream is_Renamed);
        }

        //UPGRADE_NOTE: Field 'EnclosingInstance' was added to class 'DASCommand' to access its enclosing instance. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1019'"
        private class DASCommand : Command
        {
            public DASCommand(DConnect2 enclosingInstance)
            {
                InitBlock(enclosingInstance);
            }

            private void InitBlock(DConnect2 enclosingInstance)
            {
                this.enclosingInstance = enclosingInstance;
            }

            private DConnect2 enclosingInstance;

            public DConnect2 Enclosing_Instance
            {
                get { return enclosingInstance; }
            }

            internal DAS das = new DAS();

            public virtual void process(Stream is_Renamed)
            {
                das.parse(is_Renamed);
            }
        }

        /// <summary> Returns the DDS object from the dataset referenced by this object's URL.
        /// The DDS object is referred to by appending `.dds' to the end of a OPeNDAP
        /// URL.
        /// 
        /// </summary>
        /// <returns> the DDS associated with the referenced dataset.
        /// </returns>
        /// <throws>  MalformedURLException if the URL given to the constructor </throws>
        /// <summary>                               has an error
        /// </summary>
        /// <throws>  IOException           if an error connecting to the remote server </throws>
        /// <throws>  ParseException        if the DDS parser returned an error </throws>
        /// <throws>  DDSException          on an error constructing the DDS </throws>
        /// <throws>  DAP2Exception         if an error returned by the remote server </throws>
        public virtual DDS getDDS()
        {
            return getDDS("");
        }

        /// <summary> Returns the DDS object from the dataset referenced by this object's URL.
        /// The DDS object is referred to by appending `.dds' to the end of a OPeNDAP
        /// URL.
        /// </summary>
        /// <param name="CE">      The constraint expression to be applied to this request by the
        /// server.  This is combined with any CE given in the constructor.
        /// 
        /// </param>
        /// <returns> the DDS associated with the referenced dataset.
        /// </returns>
        /// <throws>  MalformedURLException if the URL given to the constructor </throws>
        /// <summary>                               has an error
        /// </summary>
        /// <throws>  IOException           if an error connecting to the remote server </throws>
        /// <throws>  ParseException        if the DDS parser returned an error </throws>
        /// <throws>  DDSException          on an error constructing the DDS </throws>
        /// <throws>  DAP2Exception         if an error returned by the remote server </throws>
        public virtual DDS getDDS(String CE)
        {
            DDSCommand command = new DDSCommand(this);
            openConnection(urlString + ".dds" + getCompleteCE(CE), command);
            return command.dds;
        }

        //UPGRADE_NOTE: Field 'EnclosingInstance' was added to class 'DDSCommand' to access its enclosing instance. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1019'"
        private class DDSCommand : Command
        {
            public DDSCommand(DConnect2 enclosingInstance)
            {
                InitBlock(enclosingInstance);
            }

            private void InitBlock(DConnect2 enclosingInstance)
            {
                this.enclosingInstance = enclosingInstance;
            }

            private DConnect2 enclosingInstance;

            public DConnect2 Enclosing_Instance
            {
                get { return enclosingInstance; }
            }

            internal DDS dds = new DDS();

            public virtual void process(Stream is_Renamed)
            {
                dds.parse(is_Renamed);
            }
        }

        /// <summary> Use some sense when assembling the CE. Since this DConnect
        /// object may have constructed using a CE, any new CE will
        /// have to be integrated into it for subsequent requests.
        /// Try to do this in a sensible manner!
        /// 
        /// </summary>
        /// <param name="CE">The new CE from the client.
        /// </param>
        /// <returns> The complete CE (the one this object was built
        /// with integrated with the clients)
        /// </returns>
        private String getCompleteCE(String CE)
        {
            String localProjString, localSelString;
            int selIndex = CE.IndexOf('&');
            if (selIndex != - 1)
            {
                if (CE.IndexOf('?') == 0)
                    localProjString = CE.Substring(1, (selIndex) - (1));
                else
                    localProjString = CE.Substring(0, (selIndex) - (0));

                localSelString = CE.Substring(selIndex);
            }
            else
            {
                if (CE.IndexOf('?') == 0)
                    localProjString = CE.Substring(1);
                else
                    localProjString = CE;
                localSelString = "";
            }

            String ce = projString;

            if (!localProjString.Equals(""))
            {
                if (!ce.Equals("") && localProjString.IndexOf(',') != 0)
                    ce += ",";
                ce += localProjString;
            }

            if (!selString.Equals(""))
            {
                if (selString.IndexOf('&') != 0)
                    ce += "&";
                ce += selString;
            }

            if (!localSelString.Equals(""))
            {
                if (localSelString.IndexOf('&') != 0)
                    ce += "&";
                ce += localSelString;
            }

            if (ce.IndexOf('?') != 0)
            {
                ce = "?" + ce;
            }

            if (false)
            {
                Console.Out.WriteLine("projString: '" + projString + "'");
                Console.Out.WriteLine("localProjString: '" + localProjString + "'");
                Console.Out.WriteLine("selString: '" + selString + "'");
                Console.Out.WriteLine("localSelString: '" + localSelString + "'");
                Console.Out.WriteLine("Complete CE: " + ce);
            }
            return (ce);
        }

        /// <summary> Returns the DDS object from the dataset referenced by this object's URL.
        /// The DDS object is referred to by appending `.ddx' to the end of a OPeNDAP
        /// URL. The server should send back a DDX (A DDS in XML format) which
        /// will get parsed here (locally) and a new DDS instantiated using
        /// the DDSXMLParser.
        /// 
        /// </summary>
        /// <returns> the DDS associated with the referenced dataset.
        /// </returns>
        /// <throws>  MalformedURLException if the URL given to the constructor </throws>
        /// <summary>                               has an error
        /// </summary>
        /// <throws>  IOException           if an error connecting to the remote server </throws>
        /// <throws>  ParseException        if the DDS parser returned an error </throws>
        /// <throws>  DDSException          on an error constructing the DDS </throws>
        /// <throws>  DAP2Exception         if an error returned by the remote server </throws>
        /// <opendap.ddx.experimental>  </opendap.ddx.experimental>
        public virtual DDS getDDX()
        {
            return (getDDX(""));
        }


        /// <summary> Returns the DDS object from the dataset referenced by this object's URL.
        /// The DDS object is referred to by appending `.ddx' to the end of a OPeNDAP
        /// URL. The server should send back a DDX (A DDS in XML format) which
        /// will get parsed here (locally) and a new DDS instantiated using
        /// the DDSXMLParser.
        /// 
        /// </summary>
        /// <param name="CE">      The constraint expression to be applied to this request by the
        /// server.  This is combined with any CE given in the constructor.
        /// </param>
        /// <returns> the DDS associated with the referenced dataset.
        /// </returns>
        /// <throws>  MalformedURLException if the URL given to the constructor </throws>
        /// <summary>                               has an error
        /// </summary>
        /// <throws>  IOException           if an error connecting to the remote server </throws>
        /// <throws>  ParseException        if the DDS parser returned an error </throws>
        /// <throws>  DDSException          on an error constructing the DDS </throws>
        /// <throws>  DAP2Exception         if an error returned by the remote server </throws>
        /// <opendap.ddx.experimental>  </opendap.ddx.experimental>
        public virtual DDS getDDX(String CE)
        {
            DDXCommand command = new DDXCommand(this);
            openConnection(urlString + ".ddx" + getCompleteCE(CE), command);
            return command.dds;
        }

        //UPGRADE_NOTE: Field 'EnclosingInstance' was added to class 'DDXCommand' to access its enclosing instance. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1019'"
        private class DDXCommand : Command
        {
            public DDXCommand(DConnect2 enclosingInstance)
            {
                InitBlock(enclosingInstance);
            }

            private void InitBlock(DConnect2 enclosingInstance)
            {
                this.enclosingInstance = enclosingInstance;
            }

            private DConnect2 enclosingInstance;

            public DConnect2 Enclosing_Instance
            {
                get { return enclosingInstance; }
            }

            internal DDS dds = new DDS();

            public virtual void process(Stream is_Renamed)
            {
                dds.parseXML(is_Renamed, false);
            }
        }

        /// <summary> Returns the DataDDS object from the dataset referenced by this object's URL.
        /// The DDS object is referred to by appending `.ddx' to the end of a OPeNDAP
        /// URL. The server should send back a DDX (A DDS in XML format) which
        /// will get parsed here (locally) and a new DDS instantiated using
        /// the DDSXMLParser.
        /// 
        /// </summary>
        /// <returns> the DataDDS associated with the referenced dataset.
        /// </returns>
        /// <throws>  MalformedURLException if the URL given to the constructor </throws>
        /// <summary>                               has an error
        /// </summary>
        /// <throws>  IOException           if an error connecting to the remote server </throws>
        /// <throws>  ParseException        if the DDS parser returned an error </throws>
        /// <throws>  DDSException          on an error constructing the DDS </throws>
        /// <throws>  DAP2Exception         if an error returned by the remote server </throws>
        /// <opendap.ddx.experimental>  </opendap.ddx.experimental>
        public virtual DataDDS getDataDDX()
        {
            return getDataDDX("", new DefaultFactory());
        }


        /// <summary> Returns the DataDDS object from the dataset referenced by this object's URL.
        /// The DDS object is referred to by appending `.ddx' to the end of a OPeNDAP
        /// URL. The server should send back a DDX (A DDS in XML format) which
        /// will get parsed here (locally) and a new DDS instantiated using
        /// the DDSXMLParser.
        /// 
        /// </summary>
        /// <param name="CE">The constraint expression to use for this request.
        /// </param>
        /// <returns> the DataDDS associated with the referenced dataset.
        /// </returns>
        /// <throws>  MalformedURLException if the URL given to the constructor </throws>
        /// <summary>                               has an error
        /// </summary>
        /// <throws>  IOException           if an error connecting to the remote server </throws>
        /// <throws>  ParseException        if the DDS parser returned an error </throws>
        /// <throws>  DDSException          on an error constructing the DDS </throws>
        /// <throws>  DAP2Exception         if an error returned by the remote server </throws>
        /// <opendap.ddx.experimental>  </opendap.ddx.experimental>
        public virtual DataDDS getDataDDX(String CE)
        {
            return getDataDDX(CE, new DefaultFactory());
        }

        /// <summary> Returns the DataDDS object from the dataset referenced by this object's URL.
        /// The DDS object is referred to by appending `.ddx' to the end of a OPeNDAP
        /// URL. The server should send back a DDX (A DDS in XML format) which
        /// will get parsed here (locally) and a new DDS instantiated using
        /// the DDSXMLParser.
        /// 
        /// </summary>
        /// <param name="CE"> The constraint expression to use for this request.
        /// </param>
        /// <param name="btf">The <code>BaseTypeFactory</code> to build the member
        /// variables in the DDS with.
        /// </param>
        /// <returns> the DataDDS associated with the referenced dataset.
        /// </returns>
        /// <throws>  MalformedURLException if the URL given to the constructor </throws>
        /// <summary>                               has an error
        /// </summary>
        /// <throws>  IOException           if an error connecting to the remote server </throws>
        /// <throws>  ParseException        if the DDS parser returned an error </throws>
        /// <throws>  DDSException          on an error constructing the DDS </throws>
        /// <throws>  DAP2Exception         if an error returned by the remote server </throws>
        /// <opendap.ddx.experimental>  </opendap.ddx.experimental>
        /// <seealso cref="BaseTypeFactory">
        /// </seealso>
        public virtual DataDDS getDataDDX(String CE, IBaseTypeFactory btf)
        {
            DataDDXCommand command = new DataDDXCommand(this, btf);
            openConnection(urlString + ".ddx" + getCompleteCE(CE), command);
            return command.dds;
        }

        //UPGRADE_NOTE: Field 'EnclosingInstance' was added to class 'DataDDXCommand' to access its enclosing instance. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1019'"
        private class DataDDXCommand : Command
        {
            private void InitBlock(DConnect2 enclosingInstance)
            {
                this.enclosingInstance = enclosingInstance;
            }

            private DConnect2 enclosingInstance;

            public DConnect2 Enclosing_Instance
            {
                get { return enclosingInstance; }
            }

            internal DataDDS dds;

            internal DataDDXCommand(DConnect2 enclosingInstance, IBaseTypeFactory btf)
            {
                InitBlock(enclosingInstance);
                dds = new DataDDS(Enclosing_Instance.ver, btf);
            }

            public virtual void process(Stream is_Renamed)
            {
                dds.parseXML(is_Renamed, false);
            }
        }


        /// <summary> Returns the `Data object' from the dataset referenced by this object's
        /// URL given the constraint expression CE. Note that the Data object is
        /// really just a DDS object with data bound to the variables. The DDS will
        /// probably contain fewer variables (and those might have different
        /// types) than in the DDS returned by getDDS() because that method returns
        /// the entire DDS (but without any data) while this method returns
        /// only those variables listed in the projection part of the constraint
        /// expression.
        /// <p/>
        /// Note that if CE is an empty String then the entire dataset will be
        /// returned, unless a "sticky" CE has been specified in the constructor.
        /// 
        /// </summary>
        /// <param name="CE">      The constraint expression to be applied to this request by the
        /// server.  This is combined with any CE given in the constructor.
        /// </param>
        /// <param name="statusUI">the <code>StatusUI</code> object to use for GUI updates
        /// and user cancellation notification (may be null).
        /// </param>
        /// <param name="btf">     The <code>BaseTypeFactory</code> to build the member
        /// variables in the DDS with.
        /// </param>
        /// <returns> The <code>DataDDS</code> object that results from applying the
        /// given CE, combined with this object's sticky CE, on the referenced
        /// dataset.
        /// </returns>
        /// <throws>  MalformedURLException if the URL given to the constructor </throws>
        /// <summary>                               has an error
        /// </summary>
        /// <throws>  IOException           if any error connecting to the remote server </throws>
        /// <throws>  ParseException        if the DDS parser returned an error </throws>
        /// <throws>  DDSException          on an error constructing the DDS </throws>
        /// <throws>  DAP2Exception         if any error returned by the remote server </throws>
        public virtual DataDDS getData(String CE, IStatusUI statusUI, IBaseTypeFactory btf)
        {
            String urls = urlString + ".dods" + getCompleteCE(CE);

            DataDDS dds = new DataDDS(ver, btf);
            DataDDSCommand command = new DataDDSCommand(this, dds, statusUI);
            openConnection(urls, command);

            return command.dds;
        }

        //UPGRADE_NOTE: Field 'EnclosingInstance' was added to class 'DataDDSCommand' to access its enclosing instance. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1019'"
        private class DataDDSCommand : Command
        {
            private void InitBlock(DConnect2 enclosingInstance)
            {
                this.enclosingInstance = enclosingInstance;
            }

            private DConnect2 enclosingInstance;

            public DConnect2 Enclosing_Instance
            {
                get { return enclosingInstance; }
            }

            internal DataDDS dds;
            internal IStatusUI statusUI;

            internal DataDDSCommand(DConnect2 enclosingInstance, DataDDS dds, IStatusUI statusUI)
            {
                InitBlock(enclosingInstance);
                this.dds = dds;
                this.statusUI = statusUI;
            }

            public virtual void process(Stream is_Renamed)
            {
                dds.parse(new HeaderInputStream(is_Renamed));
                dds.readData(is_Renamed, statusUI);
            }
        }


        public virtual DataDDS getData(String CE)
        {
            return getData(CE, null, new DefaultFactory());
        }

        /// <summary> Returns the `Data object' from the dataset referenced by this object's
        /// URL given the constraint expression CE. Note that the Data object is
        /// really just a DDS object with data bound to the variables. The DDS will
        /// probably contain fewer variables (and those might have different
        /// types) than in the DDS returned by getDDS() because that method returns
        /// the entire DDS (but without any data) while this method returns
        /// only those variables listed in the projection part of the constraint
        /// expression.
        /// <p/>
        /// Note that if CE is an empty String then the entire dataset will be
        /// returned, unless a "sticky" CE has been specified in the constructor.
        /// <p/>
        /// <p/>
        /// This method uses the 2 step method for aquiring data from a server using
        /// a DDX and a BLOB. First, a DDX (an XML representation of a DDS) is requested.
        /// The DDX is parsed and a DataDDS is created.
        /// The DDX contains a URL that points to the servers BLOB service. The BLOB
        /// service returns only the serialized binary content of the DataDDS. The DataDDS
        /// then deserializes the BLOB and fills itself with data.
        /// 
        /// </summary>
        /// <param name="CE">      The constraint expression to be applied to this request by the
        /// server.  This is combined with any CE given in the constructor.
        /// </param>
        /// <param name="statusUI">the <code>StatusUI</code> object to use for GUI updates
        /// and user cancellation notification (may be null).
        /// </param>
        /// <returns> The <code>DataDDS</code> object that results from applying the
        /// given CE, combined with this object's sticky CE, on the referenced
        /// dataset.
        /// </returns>
        /// <throws>  MalformedURLException if the URL given to the constructor </throws>
        /// <summary>                               has an error
        /// </summary>
        /// <throws>  IOException           if any error connecting to the remote server </throws>
        /// <throws>  ParseException        if the DDS parser returned an error </throws>
        /// <throws>  DDSException          on an error constructing the DDS </throws>
        /// <throws>  DAP2Exception         if any error returned by the remote server </throws>
        /// <opendap.ddx.experimental>  </opendap.ddx.experimental>
        /// <summary> 
        /// public DataDDS getDDXData(String CE, StatusUI statusUI, BaseTypeFactory btf) throws MalformedURLException, IOException,
        /// ParseException, DDSException, DAP2Exception {
        /// String urls = urlString + ".ddx" + getCompleteCE(CE);
        /// DataDDS dds = new DataDDS(ver, btf);
        /// DataDDXCommand command = new DataDDXCommand(dds, statusUI);
        /// openConnection(urls, command);
        /// return command.dds;
        /// }
        /// private class DataDDXCommand implements Command {
        /// DataDDS dds;
        /// StatusUI statusUI;
        /// DataDDXCommand(DataDDS dds, StatusUI statusUI) {
        /// this.dds = dds;
        /// this.statusUI = statusUI;
        /// }
        /// /*
        /// Returns the `Data object' from the dataset referenced by this object's
        /// URL given the constraint expression CE. Note that the Data object is
        /// really just a DDS object with data bound to the variables. The DDS will
        /// probably contain fewer variables (and those might have different
        /// types) than in the DDS returned by getDDS() because that method returns
        /// the entire DDS (but without any data) while this method returns
        /// only those variables listed in the projection part of the constraint
        /// expression.
        /// <p/>
        /// Note that if CE is an empty String then the entire dataset will be
        /// returned, unless a "sticky" CE has been specified in the constructor.
        /// <p/>
        /// <p/>
        /// This method uses the 2 step method for aquiring data from a server using
        /// a DDX and a BLOB. First, a DDX (an XML representation of a DDS) is requested.
        /// The DDX is parsed and a DataDDS is created.
        /// The DDX contains a URL that points to the servers BLOB service. The BLOB
        /// service returns only the serialized binary content of the DataDDS. The DataDDS
        /// then deserializes the BLOB and fills itself with data.
        /// public void process(InputStream is) throws DAP2Exception, ParseException, IOException {
        /// dds.parseXML(is, false);    // read the DDX
        /// getBlobData(dds, statusUI);
        /// }
        /// }  
        /// </summary>
        /// <summary> Opens the BLOB uRL in the DDS supplied and deserializes that binary content
        /// sent from the server cooresponding to the DDS.
        /// <p/>
        /// <p/>
        /// This method is the 2nd step in the 2 step process for aquiring data from a server using
        /// a DDX and a BLOB. First, a DDX (an XML representation of a DDS) is requested.
        /// The DDX is parsed and a DataDDS is created.
        /// The DDX contains a URL that points to the servers BLOB service. The BLOB
        /// service returns only the serialized binary content of the DataDDS. The DataDDS
        /// then deserializes the BLOB and fills itself with data.
        /// 
        /// </summary>
        /// <param name="dds">     The DDS containing the BLOB URL and into which the BLOB
        /// (serialized binary content) will be deserialized.
        /// </param>
        /// <param name="statusUI">the <code>StatusUI</code> object to use for GUI updates
        /// and user cancellation notification (may be null).
        /// </param>
        /// <throws>  MalformedURLException if the URL given to the constructor </throws>
        /// <summary>                               has an error
        /// </summary>
        /// <throws>  IOException           if any error connecting to the remote server </throws>
        /// <throws>  ParseException        if the DDS parser returned an error </throws>
        /// <throws>  DDSException          on an error constructing the DDS </throws>
        /// <throws>  DAP2Exception         if any error returned by the remote server </throws>
        /// <summary> 
        /// private void getBlobData(DataDDS dds, StatusUI statusUI)
        /// throws MalformedURLException, IOException,
        /// ParseException, DDSException, DAP2Exception {
        /// /* boolean dumpStreamErr = false; // opendap.util.Debug.isSet("dumpStreamErr");
        /// System.out.println("dds.getBlobURL(): " + dds.getBlobContentID());
        /// if (dds.getBlobContentID() == null) {
        /// throw new MalformedURLException("Blob URL was 'null'. " +
        /// "This may indicate that this OPeNDAP Server does not support the full use of DDX.");
        /// }
        /// URL blobURL = new URL(dds.getBlobContentID());
        /// System.out.println("Opening BLOB URL: " + blobURL);
        /// InputStream is = openConnection(blobURL);
        /// try {
        /// dds.readData(is, statusUI); // read the data!
        /// } catch (Throwable e) {
        /// System.out.println("DConnect dds.readData problem with: " + blobURL + "\nStack Trace:");
        /// e.printStackTrace(System.out);
        /// throw new DAP2Exception("Connection problem when reading: " + blobURL + "\n" +
        /// "Error Message - " + e.toString());
        /// } finally {
        /// is.close();  // stream is always closed even if parse() throws exception
        /// if (connection instanceof HttpURLConnection)
        /// ((HttpURLConnection) connection).disconnect();
        /// }
        /// } 
        /// </summary>
        /// <summary> Returns the `Data object' from the dataset referenced by this object's
        /// URL given the constraint expression CE. Note that the Data object is
        /// really just a DDS object with data bound to the variables. The DDS will
        /// probably contain fewer variables (and those might have different
        /// types) than in the DDS returned by getDDS() because that method returns
        /// the entire DDS (but without any data) while this method returns
        /// only those variables listed in the projection part of the constraint
        /// expression.
        /// <p/>
        /// Note that if CE is an empty String then the entire dataset will be
        /// returned, unless a "sticky" CE has been specified in the constructor.
        /// <p/>
        /// <p/>
        /// This method uses the 2 step method for aquiring data from a server using
        /// a DDX and a BLOB. First, a DDX (an XML representation of a DDS) is requested.
        /// The DDX is parsed and a DataDDS is created.
        /// The DDX contains a URL that points to the servers BLOB service. The BLOB
        /// service returns only the serialized binary content of the DataDDS. The DataDDS
        /// then deserializes the BLOB and fills itself with data.
        /// 
        /// </summary>
        /// <param name="url">     The complete URL of the dataset. Constraint Expression included.
        /// </param>
        /// <param name="statusUI">the <code>StatusUI</code> object to use for GUI updates
        /// and user cancellation notification (may be null).
        /// </param>
        /// <param name="btf">     The <code>BaseTypeFactory</code> to build the member
        /// variables in the DDS with.
        /// </param>
        /// <returns> The <code>DataDDS</code> object that results from applying the
        /// given CE, combined with this object's sticky CE, on the referenced
        /// dataset.
        /// </returns>
        /// <throws>  MalformedURLException if the URL given to the constructor </throws>
        /// <summary>                               has an error
        /// </summary>
        /// <throws>  IOException           if any error connecting to the remote server </throws>
        /// <throws>  ParseException        if the DDS parser returned an error </throws>
        /// <throws>  DDSException          on an error constructing the DDS </throws>
        /// <throws>  DAP2Exception         if any error returned by the remote server </throws>
        /// <opendap.ddx.experimental>  </opendap.ddx.experimental>
        /// <summary> 
        /// public DataDDS getDDXDataFromURL(URL url, StatusUI statusUI, BaseTypeFactory btf)
        /// throws IOException,
        /// ParseException, DDSException, DAP2Exception {
        /// System.out.println("Opening DDX URL: " + url);
        /// InputStream is = openConnection(url);
        /// DataDDS dds = new DataDDS(ver, btf);
        /// boolean dumpStreamErr = false; // opendap.util.Debug.isSet("dumpStreamErr");
        /// try {
        /// dds.parseXML(is, false);    // read the DDX
        /// //dds.parse(new HeaderInputStream(is));    // read the DDS header
        /// // NOTE: the HeaderInputStream will have skipped over "Data:" line
        /// } catch (Throwable e) {
        /// System.out.println("DConnect ddx.parse problem with: " + url + "\nStack Trace:");
        /// e.printStackTrace(System.out);
        /// throw new DAP2Exception("Connection problem when reading: " + url + "\n" +
        /// "Error Message - " + e.toString());
        /// } finally {
        /// is.close();  // stream is always closed even if parse() throws exception
        /// if (connection instanceof HttpURLConnection)
        /// ((HttpURLConnection) connection).disconnect();
        /// }
        /// getBlobData(dds, statusUI);
        /// return dds;
        /// } 
        /// </summary>
        /// <summary> Returns the `Data object' from the dataset referenced by this object's
        /// URL given the constraint expression CE. Note that the Data object is
        /// really just a DDS object with data bound to the variables. The DDS will
        /// probably contain fewer variables (and those might have different
        /// types) than in the DDS returned by getDDS() because that method returns
        /// the entire DDS (but without any data) while this method returns
        /// only those variables listed in the projection part of the constraint
        /// expression.
        /// <p/>
        /// Note that if CE is an empty String then the entire dataset will be
        /// returned, unless a "sticky" CE has been specified in the constructor.
        /// 
        /// </summary>
        /// <param name="CE">      The constraint expression to be applied to this request by the
        /// server.  This is combined with any CE given in the constructor.
        /// </param>
        /// <param name="statusUI">the <code>StatusUI</code> object to use for GUI updates
        /// and user cancellation notification (may be null).
        /// </param>
        /// <returns> The <code>DataDDS</code> object that results from applying the
        /// given CE, combined with this object's sticky CE, on the referenced
        /// dataset.
        /// </returns>
        /// <throws>  MalformedURLException if the URL given to the constructor </throws>
        /// <summary>                               has an error
        /// </summary>
        /// <throws>  IOException           if any error connecting to the remote server </throws>
        /// <throws>  ParseException        if the DDS parser returned an error </throws>
        /// <throws>  DDSException          on an error constructing the DDS </throws>
        /// <throws>  DAP2Exception         if any error returned by the remote server </throws>
        public virtual DataDDS getData(String CE, IStatusUI statusUI)
        {
            return getData(CE, statusUI, new DefaultFactory());
        }

        /// <summary> Returns the `Data object' from the dataset referenced by this object's
        /// URL given the constraint expression CE. Note that the Data object is
        /// really just a DDS object with data bound to the variables. The DDS will
        /// probably contain fewer variables (and those might have different
        /// types) than in the DDS returned by getDDS() because that method returns
        /// the entire DDS (but without any data) while this method returns
        /// only those variables listed in the projection part of the constraint
        /// expression.
        /// <p/>
        /// Note that if CE is an empty String then the entire dataset will be
        /// returned, unless a "sticky" CE has been specified in the constructor.
        /// <p/>
        /// <p/>
        /// This method uses the 2 step method for aquiring data from a server using
        /// a DDX and a BLOB. First, a DDX (an XML representation of a DDS) is requested.
        /// The DDX is parsed and a DataDDS is created.
        /// The DDX contains a URL that points to the servers BLOB service. The BLOB
        /// service returns only the serialized binary content of the DataDDS. The DataDDS
        /// then deserializes the BLOB and fills itself with data.
        /// 
        /// </summary>
        /// <param name="CE">      The constraint expression to be applied to this request by the
        /// server.  This is combined with any CE given in the constructor.
        /// </param>
        /// <param name="statusUI">the <code>StatusUI</code> object to use for GUI updates
        /// and user cancellation notification (may be null).
        /// </param>
        /// <returns> The <code>DataDDS</code> object that results from applying the
        /// given CE, combined with this object's sticky CE, on the referenced
        /// dataset.
        /// </returns>
        /// <throws>  MalformedURLException if the URL given to the constructor </throws>
        /// <summary>                               has an error
        /// </summary>
        /// <throws>  IOException           if any error connecting to the remote server </throws>
        /// <throws>  ParseException        if the DDS parser returned an error </throws>
        /// <throws>  DDSException          on an error constructing the DDS </throws>
        /// <throws>  DAP2Exception         if any error returned by the remote server </throws>
        /// <opendap.ddx.experimental>  </opendap.ddx.experimental>
        /// <summary> 
        /// public DataDDS getDDXData(String CE, StatusUI statusUI) throws MalformedURLException, IOException,
        /// ParseException, DDSException, DAP2Exception {
        /// return getDDXData(CE, statusUI, new DefaultFactory());
        /// } 
        /// </summary>
        /// <summary> Return the data object with no local constraint expression.  Same as
        /// <code>getData("", statusUI)</code>.
        /// 
        /// </summary>
        /// <param name="statusUI">the <code>StatusUI</code> object to use for GUI updates
        /// and user cancellation notification (may be null).
        /// </param>
        /// <returns> The <code>DataDDS</code> object that results from applying
        /// this object's sticky CE, if any, on the referenced dataset.
        /// </returns>
        /// <throws>  MalformedURLException if the URL given to the constructor </throws>
        /// <summary>                               has an error
        /// </summary>
        /// <throws>  IOException           if any error connecting to the remote server </throws>
        /// <throws>  ParseException        if the DDS parser returned an error </throws>
        /// <throws>  DDSException          on an error constructing the DDS </throws>
        /// <throws>  DAP2Exception         if any error returned by the remote server </throws>
        public DataDDS getData(IStatusUI statusUI)
        {
            return getData("", statusUI, new DefaultFactory());
        }

        /// <summary> Returns the `Data object' from the dataset referenced by this object's
        /// URL given the constraint expression CE. Note that the Data object is
        /// really just a DDS object with data bound to the variables. The DDS will
        /// probably contain fewer variables (and those might have different
        /// types) than in the DDS returned by getDDS() because that method returns
        /// the entire DDS (but without any data) while this method returns
        /// only those variables listed in the projection part of the constraint
        /// expression.
        /// <p/>
        /// Note that if CE is an empty String then the entire dataset will be
        /// returned, unless a "sticky" CE has been specified in the constructor.
        /// <p/>
        /// <p/>
        /// This method uses the 2 step method for aquiring data from a server using
        /// a DDX and a BLOB. First, a DDX (an XML representation of a DDS) is requested.
        /// The DDX is parsed and a DataDDS is created.
        /// The DDX contains a URL that points to the servers BLOB service. The BLOB
        /// service returns only the serialized binary content of the DataDDS. The DataDDS
        /// then deserializes the BLOB and fills itself with data.
        /// 
        /// </summary>
        /// <param name="statusUI">the <code>StatusUI</code> object to use for GUI updates
        /// and user cancellation notification (may be null).
        /// </param>
        /// <returns> The <code>DataDDS</code> object that results from applying the
        /// given CE, combined with this object's sticky CE, on the referenced
        /// dataset.
        /// </returns>
        /// <throws>  MalformedURLException if the URL given to the constructor </throws>
        /// <summary>                               has an error
        /// </summary>
        /// <throws>  IOException           if any error connecting to the remote server </throws>
        /// <throws>  ParseException        if the DDS parser returned an error </throws>
        /// <throws>  DDSException          on an error constructing the DDS </throws>
        /// <throws>  DAP2Exception         if any error returned by the remote server </throws>
        /// <opendap.ddx.experimental>  </opendap.ddx.experimental>
        /// <summary> 
        /// public final DataDDS getDDXData(StatusUI statusUI) throws MalformedURLException, IOException,
        /// ParseException, DDSException, DAP2Exception {
        /// return getDDXData("", statusUI, new DefaultFactory());
        /// } 
        /// </summary>
        /// <summary> A primitive parser for the MIME headers used by OPeNDAP.  This is used when
        /// reading from local sources of OPeNDAP Data objects. It is called by
        /// <code>readData</code> to simulate the important actions of the
        /// <code>URLConnection</code> MIME header parsing performed in
        /// <code>openConnection</code> for HTTP URL's.
        /// <p/>
        /// <b><i>NOTE:</b></i> Because BufferedReader seeks ahead, and therefore
        /// removescharacters from the InputStream which are needed later, and
        /// because there is no way to construct an InputStream from a
        /// BufferedReader, we have to use DataInputStream to read the header
        /// lines, which triggers an unavoidable deprecated warning from the
        /// Java compiler.
        /// 
        /// </summary>
        /// <param name="is">the InputStream to read.
        /// </param>
        /// <returns> the InputStream to read data from (after attaching any
        /// necessary decompression filters).
        /// </returns>
        /// <throws>  IOException   if any IO error. </throws>
        /// <throws>  DAP2Exception if the server returned an Error. </throws>
        private Stream parseMime(Stream is_Renamed)
        {
            // NOTE: because BufferedReader seeks ahead, and therefore removes
            // characters from the InputStream which are needed later, and
            // because there is no way to construct an InputStream from a
            // BufferedReader, we have to use DataInputStream to read the header
            // lines, which triggers an unavoidable deprecated warning from the
            // Java compiler.

            //UPGRADE_TODO: Class 'java.io.DataInputStream' was converted to 'System.IO.BinaryReader' which has a different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1073_javaioDataInputStream'"
            BinaryReader d = new BinaryReader(is_Renamed);

            String description = null;
            String encoding = null;

            // while there are more header (non-blank) lines
            String line;
            //UPGRADE_TODO: Method 'java.io.DataInputStream.readLine' was converted to 'System.IO.BinaryReader.ReadString' which has a different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1073_javaioDataInputStreamreadLine'"
            while (!(line = d.ReadString()).Equals(""))
            {
                int spaceIndex = line.IndexOf(' ');
                // all header lines should have a space in them, but if not, skip ahead
                if (spaceIndex == - 1)
                    continue;
                String header = line.Substring(0, (spaceIndex) - (0));
                String value_Renamed = line.Substring(spaceIndex + 1);

                if (header.Equals("Server:"))
                {
                    ver = new ServerVersion(value_Renamed);
                }
                else if (header.Equals("Content-Description:"))
                {
                    description = value_Renamed;
                }
                else if (header.Equals("Content-Encoding:"))
                {
                    encoding = value_Renamed;
                }
            }
            handleContentDesc(is_Renamed, description);
            return handleContentEncoding(is_Renamed, encoding);
        }

        /// <summary> This code handles the Content-Description: header for
        /// <code>openConnection</code> and <code>parseMime</code>.
        /// Throws a <code>DAP2Exception</code> if the type is
        /// <code>dods_error</code>.
        /// 
        /// </summary>
        /// <param name="is">  the InputStream to read.
        /// </param>
        /// <param name="type">the Content-Description header, or null.
        /// </param>
        /// <throws>  IOException   if any error reading from the server. </throws>
        /// <throws>  DAP2Exception if the server returned an error. </throws>
        private void handleContentDesc(Stream is_Renamed, String type)
        {
            if (type != null && type.Equals("dods_error"))
            {
                // create server exception object
                DAP2Exception ds = new DAP2Exception();
                // parse the Error object from stream and throw it
                ds.parse(is_Renamed);
                throw ds;
            }
        }

        /// <summary> This code handles the Content-type: header for
        /// <code>openConnection</code> and <code>parseMime</code>
        /// 
        /// </summary>
        /// <param name="is">      the InputStream to read.
        /// </param>
        /// <param name="encoding">the Content-type header, or null.
        /// </param>
        /// <returns> the new InputStream, after applying an
        /// <code>InflaterInputStream</code> filter if necessary.
        /// </returns>
        private Stream handleContentEncoding(Stream is_Renamed, String encoding)
        {
            if (encoding != null && encoding.Equals("deflate"))
            {
                //UPGRADE_ISSUE: Constructor 'java.util.zip.InflaterInputStream.InflaterInputStream' was not converted. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1000_javautilzipInflaterInputStream'"
                return new InflaterInputStream(is_Renamed);
            }
            else
            {
                return is_Renamed;
            }
        }
    }
}